﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting.Messaging;
using System.Web.Mvc;
using Entropy.Proxy;

namespace Entropy.Ext
{
    public class ExtFactoryProxy : IInterfacedFactoryAccess
	{
		private static readonly ConditionalWeakTable<IExtFactory, WebViewPage> PageExtTable = new ConditionalWeakTable<IExtFactory, WebViewPage>();

		public static WebViewPage PageByExt(IExtFactory ext)
		{
			WebViewPage result;
			if (PageExtTable.TryGetValue(ext, out result))
				return result;
			throw new Exception("Cannot find extFactory");
		}

		internal static IExtFactory Create(WebViewPage page, ISyntaxTree syntaxTree, Func<string> onPublish)
		{
			var result = new RealProxyBasedFactory().CreateProxy<IExtFactory>(null, new ExtFactoryProxy(syntaxTree, onPublish).OnInvocation);
			PageExtTable.Add(result, page);
			return result;
		}

	    readonly ISyntaxTree builder;
		private readonly Func<string> onPublish;

		public ExtFactoryProxy(ISyntaxTree builder, Func<string> onPublish)
		{
			this.builder = builder;
			this.onPublish = onPublish;
		}

		public IMethodReturnMessage OnInvocation(IMethodCallMessage msg, RemotingInvocation target)
		{
			if (msg.MethodName == "Access")
				return new ReturnMessage(this, new object[0], 0, null, msg);

			// we dont mock IInterfacedFactory
            if (msg.MethodBase.DeclaringType == typeof(IInterfacedFactoryAccess))
				return new ReturnMessage(null, new object[0], 0, null, msg);

		    var retType = ((MethodInfo)msg.MethodBase).ReturnType;
			var paramz = msg.MethodBase.GetParameters();
			var args = new List<CallArgument>();
			Debug.Assert(paramz.Length == msg.InArgCount);

			for (int i = 0; i < msg.InArgs.Length; i++)
			{
				var inArg = msg.InArgs[i];
				if (!paramz[i].HasDefaultValue || paramz[i].DefaultValue != inArg)
					args.Add(new CallArgument { 
						ParamName = paramz[i].Name, 
						Value = inArg,
						AsJson = paramz[i].GetCustomAttribute<JsConfigAttribute>() != null
					});
			}

		    var result = InterfacedFactory.Create(retType, builder, onPublish, args);
			return new ReturnMessage(result, new object[0], 0, null, msg);
		}

        object IInterfacedFactoryAccess.Access()
		{
			return this;
		}
	}

}